package com.insight.common.aspect;

import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.yuanqiao.insight.common.util.common.LocalCacheUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import com.insight.common.api.CommonAPI;
import com.insight.common.constant.CommonConstant;
import com.insight.common.system.vo.LoginUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.stream.Collectors;

/**
 * SSO切面，用于处理SSO相关的同步和登出逻辑。
 */
@Aspect
@Slf4j
@Component
@ConditionalOnProperty(name = "jeecg.shiro.realm", havingValue = "oauth2")
public class SsoAspect {

    @Autowired
    private CommonAPI commonAPI;

    private static final LocalCacheUtils cacheUtils = LocalCacheUtils.getInstance();

    /**
     * 定义切点，匹配带有@SynMenu注解的方法。
     */
    @Pointcut("@annotation(com.insight.common.aspect.annotation.SynMenu)")
    public void pointCut() {

    }

    /**
     * 环绕通知，同步菜单到SSO。
     * @param joinPoint 切点
     * @return 方法执行结果
     * @throws Throwable 异常
     */
    @Around("pointCut()") // 根据你的包结构修改
    public Object syncMenuAround(ProceedingJoinPoint joinPoint) throws Throwable {
        // 获取请求对象
        ServletRequestAttributes attributes =
                (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = (attributes != null) ? attributes.getRequest() : null;
        String auth = "";

        if (request != null) {
            auth = request.getHeader(CommonConstant.X_ACCESS_TOKEN);
        } else {
            return joinPoint.proceed();
        }

        // 获取方法签名
        Object[] paramValues = joinPoint.getArgs();
        if (paramValues == null || paramValues.length == 0) {
            return joinPoint.proceed(); // 安全兜底
        }

        Object param = paramValues[0];
        List<JSONObject> permissions = new ArrayList<>();

        if (param instanceof String) {
            // 删除操作，要在方法执行之前获取菜单
            String[] ids = ((String) param).split(",");
            if (ids.length > 0) {
                permissions = commonAPI.getPermissionData(Arrays.asList(ids));
            }
        }

        // 执行目标方法
        Object result = joinPoint.proceed();

        // 非 String 类型：新增/更新操作，在方法执行后构建权限数据
        if (!(param instanceof String)) {
            try {
                JSONObject per = JSONObject.parseObject(JSON.toJSONString(param));
                if (per == null) return result;

                JSONObject mappingMenu = per.getJSONObject("mappingMenu");
                if (mappingMenu == null) return result;

                // 获取 clientId
                Object clientCache = cacheUtils.getValue("oauth2_clientId");
                JSONObject clientId = clientCache instanceof String ?
                        JSON.parseObject((String) clientCache) :
                        (clientCache instanceof JSONObject ? (JSONObject) clientCache : new JSONObject());

                String platformType = per.getString("platformType");
                if (platformType != null && clientId != null) {
                    mappingMenu.put("clientId", clientId.get(platformType));
                }
                permissions.add(mappingMenu);
            } catch (Exception e) {
                log.warn("构建同步菜单数据时发生异常，param: {}", param, e);
                // 不中断主流程
            }
        }

        // 同步到 SSO
        try {
            log.info("同步菜单开始");
            Object ssoUrlObj = cacheUtils.getValue("oauth2_ssoUrl");
            Object synMenuObj = cacheUtils.getValue("oauth2_synMenu");

            // 检查必要配置
            if (ssoUrlObj == null || synMenuObj == null || auth == null || auth.isEmpty()) {
                log.warn("同步菜单跳过：缺少必要参数 ssoUrl: {}, synMenu: {}, auth: {}", ssoUrlObj, synMenuObj, auth);
                return result;
            }

            String ssoUrl = ssoUrlObj.toString().trim();
            String synMenu = synMenuObj.toString().trim();

            if (ssoUrl.isEmpty() || synMenu.isEmpty()) {
                log.warn("同步菜单跳过：URL 为空 ssoUrl: '{}', synMenu: '{}'", ssoUrl, synMenu);
                return result;
            }

            String url = ssoUrl.endsWith("/") ? ssoUrl + synMenu : ssoUrl + "/" + synMenu;

            String requestBody = JSONObject.toJSONString(permissions);
            HttpResponse response = HttpUtil.createPost(url)
                    .body(requestBody)
                    .bearerAuth(auth)
                    .timeout(5000)
                    .execute();

            if (!response.isOk()) {
                log.error("同步菜单失败，HTTP状态码: {}, 响应: {}", response.getStatus(), response.body());
                return result;
            }

            String body = response.body();
            if (body == null || body.isEmpty()) {
                log.error("同步菜单失败：SSO 返回空响应");
                return result;
            }

            JSONObject res = JSON.parseObject(body);
            if (res == null || res.getInteger("code") == null || !res.getInteger("code").equals(200)) {
                String msg = res != null ? res.getString("message") : "未知错误";
                log.error("同步菜单失败：{}", msg);
            } else {
                log.info("同步菜单成功");
            }
        } catch (Exception e) {
            log.error("同步菜单异常", e);
        }

        return result;
    }

    /**
     * 环绕通知，处理用户登出后的SSO同步。
     * @param pjp 切点
     * @return 重定向地址
     * @throws Throwable 异常
     */
    @Around("execution(public * com.insight.modules.system.controller.LoginController.logout(..)))")
    public Object afterReturning(ProceedingJoinPoint pjp) throws Throwable {
        pjp.proceed();
        // oauth2
        ServletRequestAttributes attributes =
                (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = (attributes != null) ? attributes.getRequest() : null;

        // 安全获取 ssoUrl
        Object ssoUrlObj = cacheUtils.getValue("oauth2_ssoUrl");
        String ssoUrl = ssoUrlObj != null ? ssoUrlObj.toString() : "";
        ssoUrl = ssoUrl.trim();

        if (request != null) {
            log.info("退出登录");
            try {
                String token = request.getHeader(CommonConstant.X_ACCESS_TOKEN);

                // 安全获取 logout 路径
                Object logoutObj = cacheUtils.getValue("oauth2_logout");
                String logout = logoutObj != null ? logoutObj.toString() : "";
                logout = logout.trim();

                // 只有 ssoUrl 和 logout 都非空才发起请求
                if (!ssoUrl.isEmpty() && !logout.isEmpty()) {
                    String url = ssoUrl.endsWith("/") ? ssoUrl + logout : ssoUrl + "/" + logout;

                    // 添加超时，防止阻塞
                    String body = HttpUtil.createGet(url)
                            .bearerAuth(token)  // token 可能为 null，Hutool 会处理
                            .timeout(5000)
                            .execute()
                            .body();

                    // body 可能为 null
                    if (body != null && !body.isEmpty()) {
                        JSONObject res = JSON.parseObject(body);
                        if (res != null
                                && res.getInteger("code") != null
                                && !res.getInteger("code").equals(200)) {
                            String message = res.getString("message");
                            log.error("退出登录失败：{}", message != null ? message : "未知错误");
                        }
                    } else {
                        log.error("退出登录失败：SSO 返回空响应");
                    }
                } else {
                    if (ssoUrl.isEmpty()) log.warn("ssoUrl 为空，跳过 SSO 退出请求");
                    if (logout.isEmpty()) log.warn("oauth2_logout 配置为空，跳过 SSO 退出请求");
                }
            } catch (Exception e) {
                log.error("退出登录失败", e);
            }
        }

        // 安全获取 redirectUrl
        Object redirectObj = cacheUtils.getValue("oauth2_redirectUrl");
        String redirectUrl = redirectObj != null ? redirectObj.toString() : "";
        redirectUrl = redirectUrl.trim();

        log.info("开始重定向：{}", redirectUrl);

        // 防止 ssoUrl 或 redirectUrl 为空导致拼接出问题
        if (ssoUrl.isEmpty() || redirectUrl.isEmpty()) {
            log.warn("重定向跳过：ssoUrl='{}', redirectUrl='{}'", ssoUrl, redirectUrl);
            return ""; // 或返回默认页？
        }

        String fullRedirect = ssoUrl + (ssoUrl.endsWith("/") ? "" : "/") + redirectUrl;
        return "redirect:" + fullRedirect;
    }

    /**
     * 前置通知，设置用户角色权限。
     * @param joinPoint 切点
     */
    @Before("execution(public * com.insight.modules.system.controller.SysPermissionController.getUserPermissionByToken(..)))")
    public void setRolePermission(JoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        Integer platformType = (Integer) args[0];

        ServletRequestAttributes attributes =
                (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes == null) {
            return;
        }
        HttpServletRequest request = attributes.getRequest();
        String token = request.getHeader(CommonConstant.X_ACCESS_TOKEN);
        com.alibaba.fastjson.JSONObject clientIdObj = JSON.parseObject(String.valueOf(Optional.ofNullable(cacheUtils.getValue("oauth2_clientId")).orElse("{}")));
        String ssoUrl = String.valueOf(Optional.ofNullable(cacheUtils.getValue("oauth2_ssoUrl")).orElse(""));
        String getUserMenu = String.valueOf(Optional.ofNullable(cacheUtils.getValue("oauth2_getUserMenu")).orElse(""));
        String clientId = clientIdObj.getString(String.valueOf(platformType));
        String response = HttpUtil
                .createGet(String.format("%s%s?clientId=%s", ssoUrl, getUserMenu, clientId))
                .bearerAuth(token)
                .execute()
                .body();
        LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
        Set<String> roleSet = commonAPI.queryUserRoles(loginUser.getUsername());
        if (StringUtils.isNotBlank(response)) {
            com.alibaba.fastjson.JSONObject responseObj = com.alibaba.fastjson.JSONObject.parseObject(response);
            if (responseObj.getInteger("code") == 200) {
                List<String> ids = JSON.parseArray(responseObj.getString("data"))
                        .stream()
                        .map(o -> JSON.parseObject(o.toString()).getString("menuCode"))
                        .collect(Collectors.toList());
                    commonAPI.setUserPermission(roleSet, ids);
            }
        }
    }
}
